package com.boardour.toupin.show;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;

import com.boardour.toupin.VideoUtils;
import com.boardour.wifip2p.packet.BytesReceivePacket;

import java.io.Closeable;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

public class ShowDecode implements Runnable, Closeable {
    private static final String MIME_TYPE = VideoUtils.MIME_TYPE;
    private static final int DEQUEUE_TIME = 50;
    private MediaFormat format;
    private MediaCodec mediaCodec;
    private AtomicBoolean isStart = new AtomicBoolean(false);
    private AtomicBoolean isWait = new AtomicBoolean(false);
    private volatile ConcurrentLinkedQueue<BytesReceivePacket> queue = new ConcurrentLinkedQueue<>();
    private Object lock = new Object();


    /**
     * 开始
     *
     * @param surface
     */
    public void start(Surface surface) {
        if (isStart.compareAndSet(false, true)) {
            try {
                format = MediaFormat.createVideoFormat(MIME_TYPE, VideoUtils.DECODE_WIDTH, VideoUtils.DECODE_HEIGHT);
                format.setInteger(MediaFormat.KEY_BIT_RATE, VideoUtils.BIT_RATE);
                format.setInteger(MediaFormat.KEY_FRAME_RATE, VideoUtils.FRAME_RATE);
                format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, VideoUtils.I_FRAME_INTERVAL);
                format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
                mediaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
                mediaCodec.configure(format, surface, null, 0);
                mediaCodec.start();
                Executors.newCachedThreadPool().execute(this);
                Executors.newCachedThreadPool().execute(new Runnable() {
                    @Override
                    public void run() {
                        inputData();
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
                isStart.set(false);
            }
        }
    }

    @Override
    public void run() {
        Log.e("TAG", "out start ...");
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        while (isStart.get()) {
            try {
                int outIndex = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIME);
                while (outIndex >= 0) {
                    mediaCodec.releaseOutputBuffer(outIndex, true);
                    outIndex = mediaCodec.dequeueOutputBuffer(info, DEQUEUE_TIME);
                }
                SystemClock.sleep(DEQUEUE_TIME);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Log.e("TAG", "out end ...");
    }

    @Override
    public void close() {
        if (isStart.compareAndSet(true, false)) {
            release();
        }
    }

    private void release() {
        queue.clear();
        notifyLock();
        try {
            if (mediaCodec != null) {
                mediaCodec.stop();
                mediaCodec.release();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        mediaCodec = null;
    }

    private volatile boolean isFirst = true;
    public static int NAL_SPS = 7;// H264

    public void offer(BytesReceivePacket packet) {
        if (mediaCodec == null || !isStart.get()) {
            return;
        }
        if (isFirst) {
            byte[] buffer = packet.getBytes();
            int offset = 4;
            if (buffer[2] == 0x01) {
                offset = 3;
            }
            int type = buffer[offset] & 0x1F;
            if (type != NAL_SPS) {
                return;
            }
            isFirst = false;
        }
        if (queue.size() > 300) {
            queue.clear();
        }
        queue.offer(packet);
        notifyLock();
    }

    private void notifyLock() {
        if (isWait.compareAndSet(true, false)) {
            synchronized (lock) {
                lock.notifyAll();
            }
        }
    }

    private void waitLock() {
        if (isWait.compareAndSet(false, true)) {
            synchronized (lock) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private void inputData() {
        Log.e("TAG", "input start ...");
        while (isStart.get()) {
            if (queue.isEmpty()) {
                waitLock();
            }
            try {
                while (isStart.get() && !queue.isEmpty()) {
                    int index = mediaCodec.dequeueInputBuffer(DEQUEUE_TIME);
                    if (index >= 0) {
                        BytesReceivePacket packet = queue.poll();
                        ByteBuffer buffer = mediaCodec.getInputBuffer(index);
                        buffer.clear();
                        byte[] data = packet.getBytes();
                        buffer.put(data);
                        mediaCodec.queueInputBuffer(index, 0, data.length,
                                System.nanoTime() / 1000, 0);
                    } else {
                        SystemClock.sleep(DEQUEUE_TIME);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        Log.e("TAG", "input end ...");
    }

    public void reset(boolean connect) {
        Log.e("TAG", "reset ...");
        if (!connect) {
            isFirst = true;
            queue.clear();
        }
    }
}
